import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, NgZone, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { NotifyService } from '../../../service/notify.service';
import { HostConfigService, Host, WinForward, WinForwardConfigService } from '../../../service/config.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { SplitAreaDirective, SplitComponent } from 'angular-split';
import { HostTreeComponent } from '../../public/host-tree/host-tree.component';
import { NzModalService } from 'ng-zorro-antd/modal';
import { NzContextMenuService, NzDropdownMenuComponent } from 'ng-zorro-antd/dropdown';

@Component({
  selector: 'app-hostcfg',
  templateUrl: './hostcfg.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['./hostcfg.component.css']
})
export class HostcfgComponent implements OnInit, AfterViewInit {

  constructor(private forwardService: WinForwardConfigService, private modal: NzModalService, private fb: FormBuilder, public hostCfg: HostConfigService, private nzContextMenuService: NzContextMenuService, private notify: NotifyService, private ngZone: NgZone, public cd: ChangeDetectorRef) { }
  @ViewChild('content') hostContent: ElementRef;
  @ViewChild('mySplit') splitEl: SplitComponent;
  @ViewChild('hostTree') hostTree: HostTreeComponent;
  @ViewChildren(SplitAreaDirective) areasEl: QueryList<SplitAreaDirective>;
  passwordVisible = false;
  hostConfig: Host;
  baseHost: Host;
  parentHost: Host;
  hosts: Host[] = [];
  groups: Host[] = [];
  grouplist: Host[] = [];
  showAllSub = false;
  cardWidth = 200;
  winForwardList: WinForward[];

  ngOnInit(): void {
    this.hostConfig = this.hostCfg.getHostConfig();
    if (this.hostConfig) {
      this.refresh(this.hostConfig);
    }
    this.formInit();
  }
  adjustCardWidth() {
    if (!this.hostContent) return;
    let elCard = this.hostContent.nativeElement.querySelector("app-hostcard");
    if (!elCard) return;
    let style = getComputedStyle(elCard);
    let elHostList = this.hostContent.nativeElement.querySelector(".host-list");
    let n = Math.ceil(elHostList.offsetWidth / 300);
    do {
      this.cardWidth = Math.floor(elHostList.offsetWidth / n);
      this.cardWidth -= parseInt(style.marginLeft) + parseInt(style.marginRight);
      if (this.cardWidth < 200) {
        n -= 1;
        this.cardWidth = 200;
      } else {
        break;
      }
    } while (n >= 1);
    this.cd.markForCheck();
  }
  ngAfterViewInit() {
    this.areasEl.first.collapse(0);
    window.addEventListener("resize", () => this.adjustCardWidth());
    this.splitEl.dragProgress$.subscribe(x => this.ngZone.run(() => this.adjustCardWidth()));
  }
  refresh(host: Host, dataChange = false) {
    this.groups = this.hostCfg.getChildGroups(host);
    this.hosts = this.hostCfg.getChildHosts(host, this.showAllSub);
    if (this.baseHost != host) {
      this.grouplist = this.hostCfg.getHostPath(host);
      this.baseHost = host;
    }
    setTimeout(() => this.adjustCardWidth());
    if (dataChange) this.hostTree.showTreeView();
  }
  changeGroup(index) {
    let group = this.grouplist[index];
    this.refresh(group);
  }
  public isEditing = false;
  public editTitle = "";
  public tempHost: Host = this.hostCfg.newHost('host');
  public editHost: Host = this.tempHost;
  openEditDrawer(host: string | Host, template?: Host) {
    if (typeof host === 'string') {
      let type = host;
      this.editTitle = "New " + type;
      this.editHost = this.hostCfg.newHost(type.toLowerCase());
      this.parentHost = this.baseHost;
      if (template) this.hostCfg.copyHost(this.editHost, template, true);
    }
    else {
      this.editTitle = "Edit " + (this.hostCfg.isGroup(host) ? 'Group' : 'Host');
      this.editHost = this.tempHost;
      this.hostCfg.copyHost(this.editHost, host);
      this.parentHost = host.parent;
    }
    this.formReset(this.editHost);
    this.winForwardList = this.forwardService.getConfig();
    this.isEditing = true;
  }
  closeEditDrawer() {
    this.isEditing = false;
  }
  saveEditDrawer() {
    if (!this.formCheck(this.editHost)) return;
    this.hostCfg.modifyHost(this.parentHost, this.editHost);
    this.refresh(this.baseHost, true);
    this.isEditing = false;
  }
  templateHost: Host;
  onOperation(type: string, host: Host) {
    if (type == "dblclick") {
      if (this.hostCfg.isGroup(host)) this.refresh(host);
      else {
        if (this.hostCfg.isNotForward(host))
          this.notify.emitOpenTab({ type: 'ssh', host: host });
        else
          this.notify.emitOpenTab({ type: 'forward', host: host });
      }
    } else if (type == 'edit') {
      this.openEditDrawer(host);
    } else if (type == 'copy') {
      this.templateHost = host;
    } else if (type == 'editchild') {
      this.refresh(host);
    } else if (type == 'delete') {
      let baseHost = this.baseHost;
      if (host == baseHost) {
        baseHost = baseHost.parent;
      }
      this.hostCfg.deleteHost(host);
      this.refresh(baseHost, true);
      this.templateHost = null;
    } else if (type == 'ssh' || type == 'sftp' || type == 'forward') {
      this.notify.emitOpenTab({ type: type, host: host });
    }
  }
  deleteHostFromTree(host) {
    let _close = () => {
      this.onOperation('delete', host);
    }
    this.modal.confirm({
      nzTitle: 'Are you sure to delete?',
      nzContent: '',
      nzOkText: 'Yes',
      nzOkType: 'primary',
      nzOkDanger: true,
      nzOnOk: _close,
      nzCancelText: 'No'
    })
  }
  onHostTreeClick(host) {
    this.refresh(host);
  }
  onHostTreeDblClick(host) {
    if (this.hostCfg.isGroup(host)) return;
    let type = this.hostCfg.isForward(host) ? 'forward' : 'ssh';
    this.onOperation(type, host);
  }
  rightClickHost: Host;
  onHostTreeRightClick(e, menu: NzDropdownMenuComponent) {
    if (this.hostCfg.isRoot(e.host)) return;
    this.rightClickHost = e.host;
    this.nzContextMenuService.create(e.event, menu);
  }
  onCardSelected(host: Host) {
    this.groups.forEach((g) => g.selected = false);
    this.hosts.forEach((h) => h.selected = false);
    host.selected = true;
  }
  //Form
  validateForm!: FormGroup;
  formInit() {
    this.validateForm = this.fb.group({
      title: [null, [Validators.required]],
      ip: [null, [Validators.required]],
      port: [null, [Validators.required]],
      isForward: [null, [Validators.required]],
      user: [null, [Validators.required]],
      pass: [null, [Validators.required]],
      winForward: [null, []],
      script: [null, []],
      localPort: [null, [Validators.required]],
      url: [null, []]
    });
  }
  formReset(host: Host) {
    this.passwordVisible = false;
    this.validateForm.reset();
    this.validateForm.get('title')!.setValue(host.title);
    this.validateForm.get('ip')!.setValue(host.ip);
    this.validateForm.get('port')!.setValue(host.port);
    this.validateForm.get('isForward')!.setValue(host.forward);
    this.validateForm.get('user')!.setValue(host.user);
    this.validateForm.get('pass')!.setValue(host.pass);
    this.validateForm.get('winForward')!.setValue(host.winip ? host.winip + ':' + host.winport : '');
    this.validateForm.get('script')!.setValue(host.script);
    this.validateForm.get('localPort')!.setValue(host.localPort);
    this.validateForm.get('url')!.setValue(host.url);
    for (const i in this.validateForm.controls) {
      this.validateForm.controls[i].setValidators(Validators.required);
      this.validateForm.controls[i].markAsPristine();
      this.validateForm.controls[i].updateValueAndValidity();
    }
  }
  formCheck(host: Host): boolean {
    let check = [], nocheck = [];
    if (this.hostCfg.isGroup(host)) {
      check = ['title'];
      nocheck = ['ip', 'port', 'user', 'pass', 'winForward', 'localPort', 'isForward', 'script', 'url'];
    } else {
      if (this.validateForm.get('isForward')!.value) {
        check = ['title', 'ip', 'port', 'localPort'];
        nocheck = ['user', 'pass', 'winForward', 'isForward', 'script', 'url'];
      } else {
        check = ['title', 'ip', 'port', 'user', 'pass'];
        nocheck = ['localPort', 'winForward', 'isForward', 'script', 'url'];
      }
    }

    for (const i in check) {
      this.validateForm.get(check[i])!.setValidators(Validators.required);
      this.validateForm.get(check[i])!.markAsDirty();
      this.validateForm.get(check[i])!.updateValueAndValidity();
    }
    for (const i in nocheck) {
      this.validateForm.get(nocheck[i])!.clearValidators();
      this.validateForm.get(nocheck[i])!.markAsPristine();
      this.validateForm.get(nocheck[i])!.updateValueAndValidity();
    }
    if (this.validateForm.valid) {
      host.title = this.validateForm.get('title')!.value;
      host.ip = this.validateForm.get('ip')!.value;
      host.port = this.validateForm.get('port')!.value;
      host.forward = this.validateForm.get('isForward')!.value;
      host.user = this.validateForm.get('user')!.value;
      host.pass = this.validateForm.get('pass')!.value;
      host.script = this.validateForm.get('script')!.value;
      host.localPort = this.validateForm.get('localPort')!.value;
      host.url = this.validateForm.get('url')!.value;
      if (this.validateForm.get('winForward')!.value) {
        let tmp = this.validateForm.get('winForward')!.value.split(':');
        host.winip = tmp[0];
        host.winport = tmp[1];
      } else {
        delete host.winip;
        delete host.winport;
      }
    }
    return this.validateForm.valid;
  }

  isShowHostTree = false;
  toggleCollapsed(): void {
    this.isShowHostTree = !this.isShowHostTree;
    if (this.isShowHostTree) {
      this.areasEl.first.expand();
    } else {
      this.areasEl.first.collapse(0);
    }
    setTimeout(() => this.adjustCardWidth());
  }
  ExportConfig() {
    let config = JSON.stringify(this.hostCfg.getPurgedConfig(), null, 4);
    let blob = new Blob([config], { type: "text/plain" });
    let a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = "hostConfig.json";
    a.click();
  }
  ImportConfig() {
    let fileEl = document.createElement("input");
    fileEl.setAttribute("type", "file");
    fileEl.addEventListener("change", (event: any) => {
      let file = event.target.files[0];
      let reader = new FileReader();
      reader.onload = (e) => {
        this.hostConfig = this.hostCfg.reloadConfig(e.target.result);
        this.refresh(this.hostConfig, true);
      }
      reader.readAsText(file);
    })
    fileEl.dispatchEvent(new MouseEvent('click'));
  }
}
